#ifndef SELF_NODE_H
#define SELF_NODE_H

#include <cmath>
using Real = double;

class Node
{
public:
    /*----------------- member of node class ----------------*/
    Node(Real _x = 0.0, Real _y = 0.0, Real _z = 0.0){
        x = _x;
        y = _y;
        z = _z;
    }
    Node(const Node &c){                  // copy to create node
        x = c.x;
        y = c.y;
        z = c.z;
    }
    Node &operator=(const Node &c){       // overload assignment operator
        if (this != &c){                  // in case of copy itself
            x = c.x;
            y = c.y;
            z = c.z;
        }
        return *this;
    }
    Node &operator=(const Real &c) {       // overload assignment operator
        x = c;
        y = c;
        z = c;
        return *this;
    }

    ~Node()= default;

    /*-------------- overload operator of node --------------*/
    Node &operator+=(const Node &c);
    Node &operator-=(const Node &c);
    Node &operator*=(const Node &c);
    Node &operator/=(const Node &c);
    Node &operator+=(const Real &c);
    Node &operator-=(const Real &c);
    Node &operator*=(const Real &c);
    Node &operator/=(const Real &c);

    friend Node operator+(const Node &a, const Node &b);
    friend Node operator-(const Node &a, const Node &b);
    friend Node operator*(const Node &a, const Node &b);
    friend Node operator/(const Node &a, const Node &b);

    friend Node operator+(const Node &a, const Real &b);
    friend Node operator-(const Node &a, const Real &b);
    friend Node operator*(const Node &a, const Real &b);
    friend Node operator/(const Node &a, const Real &b);

    friend Node operator+(const Real &a, const Node &b);
    friend Node operator-(const Real &a, const Node &b);
    friend Node operator*(const Real &a, const Node &b);
    friend Node operator/(const Real &a, const Node &b);

    friend Node operator-(const Node &c);

public:
    Real x;
    Real y;
    Real z;
};

inline Node &Node::operator+=(const Node &c)
{
    x += c.x;
    y += c.y;
    z += c.z;
    return *this;
}
inline Node &Node::operator-=(const Node &c)
{
    x -= c.x;
    y -= c.y;
    z -= c.z;
    return *this;
}
inline Node &Node::operator*=(const Node &c)
{
    x *= c.x;
    y *= c.y;
    z *= c.z;
    return *this;
}
inline Node &Node::operator/=(const Node &c)
{
    x /= c.x;
    y /= c.y;
    z /= c.z;
    return *this;
}
inline Node &Node::operator+=(const Real &c)
{
    x += c;
    y += c;
    z += c;
    return *this;
}
inline Node &Node::operator-=(const Real &c)
{
    x -= c;
    y -= c;
    z -= c;
    return *this;
}
inline Node &Node::operator*=(const Real &c)
{
    x *= c;
    y *= c;
    z *= c;
    return *this;
}
inline Node &Node::operator/=(const Real &c)
{
    x /= c;
    y /= c;
    z /= c;
    return *this;
}

inline Node operator+(const Node &a, const Node &b)
{
    return Node(a.x + b.x, a.y + b.y, a.z + b.z);
}
inline Node operator-(const Node &a, const Node &b)
{
    return Node(a.x - b.x, a.y - b.y, a.z - b.z);
}
inline Node operator*(const Node &a, const Node &b)
{
    return Node(a.x * b.x, a.y * b.y, a.z * b.z);
}
inline Node operator/(const Node &a, const Node &b)
{
    return Node(a.x / b.x, a.y / b.y, a.z / b.z);
}

inline Node operator+(const Node &a, const Real &b)
{
    return Node(a.x + b, a.y + b, a.z + b);
}
inline Node operator-(const Node &a, const Real &b)
{
    return Node(a.x - b, a.y - b, a.z - b);
}
inline Node operator*(const Node &a, const Real &b)
{
    return Node(a.x * b, a.y * b, a.z * b);
}
inline Node operator/(const Node &a, const Real &b)
{
    return Node(a.x / b, a.y / b, a.z / b);
}

inline Node operator+(const Real &a, const Node &b)
{
    return Node(a + b.x, a + b.y, a + b.z);
}
inline Node operator-(const Real &a, const Node &b)
{
    return Node(a - b.x, a - b.y, a - b.z);
}
inline Node operator*(const Real &a, const Node &b)
{
    return Node(a * b.x, a * b.y, a * b.z);
}
inline Node operator/(const Real &a, const Node &b)
{
    return Node(a / b.x, a / b.y, a / b.z);
}

inline Node operator-(const Node &c)
{
    return Node(-c.x, -c.y, -c.z);
}

inline Real distance(const Node &a, const Node &b)
{
    Node dR;
    Real dLen;

    dR = a - b;
    dLen = dR.x*dR.x + dR.y*dR.y + dR.z*dR.z;
    return sqrt(dLen);
}

inline void normalize(Node &r)
{
    Real dLen;

    dLen = r.x*r.x + r.y*r.y + r.z*r.z;
    dLen = sqrt(dLen);

    r.x /= dLen;
    r.y /= dLen;
    r.z /= dLen;
}

inline Real Max(const Node &r)
{
    Real dMax;

    dMax = (r.x > r.y ? r.x : r.y);
    return (dMax > r.z ? dMax : r.z);
}

inline Node Abs(const Node &r)
{
    return Node(std::abs(r.x), std::abs(r.y), std::abs(r.z));
}

inline Real get_length(const Node &r)
{
    Real dLen;

    dLen = r.x*r.x + r.y*r.y + r.z*r.z;
    dLen = sqrt(dLen);

    return dLen;
}

inline Real dot_product(const Node &a, const Node &b)
{
    Real dDot;

    dDot = a.x*b.x + a.y*b.y + a.z*b.z;

    return dDot;
}

inline void cross_mult(const Node &a, const Node &b, Node &c)
{
    c.x = a.y*b.z - a.z*b.y;
    c.y = a.z*b.x - a.x*b.z;
    c.z = a.x*b.y - a.y*b.x;
}

#endif
